package parse;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.Scanner;

import partitions.Partition;
import partitions.PartitionDecorator;

public class WinningCoalitionEqualDivisionExternalities {

	static int numElements;
	static float[] powers;
	static boolean divideEqually = true;
	
	public static void main(String[] args) {
		
		System.out.println("\nexample usage: 4 \"{  0.23 0.45 0.78 0.89 }\" true\n");
		
		try{
			numElements = Integer.parseInt(args[0]);
			System.out.println("creating power structure for " + numElements + "agents");
		} catch(NumberFormatException fe){
			System.out.println("failed to specify number of agents as first argument.\n");
			System.exit(1);
		}
		powers = getPowerArray(args[1]);
		System.out.println("agent powers set to : " + floatArrayToString(powers)); 
		if(args.length >= 3){
			boolean useProportionalDivision = Boolean.parseBoolean(args[2]);
			System.out.println("divideProportionally flag set");
			divideEqually = ! useProportionalDivision;
		}
		System.out.println("divideEqually set to " + divideEqually);
		
		WinningCoalitionEqualDivisionExternalities.generate(numElements);
	}
	
	public static void generate(int numAgents){
		
		Partition partitionDomain = new Partition(numAgents);
		List<int[]> partitions = partitionDomain.getPartitions();
		
	    try {
	    	String fileName = "template_" + 
	    	numAgents + "_" +
	    	floatArrayToString(powers) + "_" +
	    	divideEqually +
	    	".txt";
	        BufferedWriter out = new BufferedWriter(new FileWriter(fileName));
	        out.write(numAgents + "\n");
	        for(int[] partition : partitions){
	        	
	        	List<int[]> partitionInListForm = PartitionDecorator.getCoalitionLists(partition);
	        	
	        	float[] payoffs = assignPayoffsToPartition(partitionInListForm);
	        	String extString = floatArrayToString(payoffs);
	        	
				String partitionString = Partition.partitionToExpandedString(partition);
				out.write(partitionString + " " + extString + "\n");
				System.out.println(partitionString + " " + extString);
			}
	        
	        out.close();
	    } catch (IOException e) {
	    	System.out.println("unable to create or open file for writing");
	    }
		
	}	
	
	
	public static float[] getPowerArray(String powerString){
		float[] powerArray = new float[numElements];

		Scanner fi = new Scanner(powerString);
		fi.useLocale(Locale.US);

		int counter = 0;
		while (fi.hasNext() && counter < powerArray.length) {
		    if (fi.hasNextInt()) {
		    	powerArray[counter] = fi.nextInt();
		    	counter++;
		    } else if (fi.hasNextDouble()) {
		        powerArray[counter] = (float)fi.nextDouble();
		        counter++;
		    } else {
		        String token = fi.next();
		        System.out.println("skipping :" + token);
		    }  
		}
		
		
		return powerArray;
	}
	
	public static String floatArrayToString(float[] payoffs){
		StringBuffer arrayString = new StringBuffer("{");
		for(int i = 0; i < numElements - 1; i++)
			arrayString.append(payoffs[i] + ", ");
		if(numElements > 0)
			arrayString.append(payoffs[payoffs.length - 1]);
		arrayString.append("}");
		return arrayString.toString();		
	}
	
	public static float[] assignPayoffsToPartition(List<int[]> partition){
		
		//get coalition with largest Power
		float[] coalitionPowers = new float[partition.size()];
		//assign power to coalitions
		int coalitionCounter = 0;
		for(int[] coalition : partition){
			for(int agent : coalition){
				coalitionPowers[coalitionCounter] += powers[agent];
			}
			coalitionCounter++;
		}
		int winningCoalitionIndex = 0;
		float winningPower = coalitionPowers[0];
		for(int i = 1; i < coalitionPowers.length; i++){
			if(coalitionPowers[i] > winningPower){
				winningCoalitionIndex = i;
				winningPower = coalitionPowers[i];
			}
		}
		//assign power to agents
		
		float[] agentPayoffs = new float[numElements];
		
		int[] winningCoalition = partition.get(winningCoalitionIndex);
		int winningCoalitionSize = winningCoalition.length;
		
		int factorialPowerMultiplier = factorial(numElements);
		
		for(int agent : winningCoalition){
			if(divideEqually){
				agentPayoffs[agent] = factorialPowerMultiplier/winningCoalitionSize;
			} else {
				agentPayoffs[agent] = powers[agent]*winningPower/winningCoalitionSize;
			}
		}
			
		return agentPayoffs;
	}
	
    // return n!
    // precondition: n >= 0 and n <= 20
    public static int factorial(int n) {
        if      (n <  0) throw new RuntimeException("Underflow error in factorial");
        else if (n > 20) throw new RuntimeException("Overflow error in factorial");
        else if (n == 0) return 1;
        else             return n * factorial(n-1);
    }	
	
}
